
package com.jiangyifen.robot.sdk;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.jiangyifen.robot.sdk.listener.DefaultRobotEventListener;
import com.jiangyifen.robot.sdk.listener.RobotEventListener;
import com.jiangyifen.robot.sdk.request.Authentication;
import com.jiangyifen.robot.sdk.request.BaseRequest;
import com.jiangyifen.robot.sdk.request.Call;
import com.jiangyifen.robot.sdk.request.Dtmf;
import com.jiangyifen.robot.sdk.request.Hangup;
import com.jiangyifen.robot.sdk.request.Register;
import com.jiangyifen.robot.sdk.request.RequestName;
import com.jiangyifen.robot.sdk.request.SayFile;
import com.jiangyifen.robot.sdk.request.SayText;
import com.jiangyifen.robot.sdk.request.SayUrl;
import com.jiangyifen.robot.sdk.request.Shutdown;
import com.jiangyifen.robot.sdk.request.Startup;
import com.jiangyifen.robot.sdk.request.StartupAll;
import com.jiangyifen.robot.sdk.request.StopTalking;
import com.jiangyifen.robot.sdk.request.Unregister;
import com.jiangyifen.robot.sdk.response.BaseResponse;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

public final class RobotSdkClient {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private Channel channel = null;

	private RobotEventListener listener = new DefaultRobotEventListener();

	public static void main(String[] args) {
		RobotSdkClient client = new RobotSdkClient();

		client.connect("127.0.0.1", 8888,"123456");
		client.addListener(new DefaultRobotEventListener());
	}

	public boolean connect(String host, int port, String secretKey) {

		if (channel != null && channel.isOpen()) {
			channel.close();
		}

		EventLoopGroup group = new NioEventLoopGroup();
		Bootstrap b = new Bootstrap();
		b.group(group).channel(NioSocketChannel.class).handler(new RobotSdkClientInitializer(host, port, listener));

		ChannelFuture f = null;
		try {
			// Make the connection attempt.
			f = b.connect(host, port).sync();
			if (f.isSuccess()) {
				channel = f.channel();
				this.authorization(secretKey);
			}
			
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}

		new Thread(() -> {
			try {
				// Wait until the connection is closed.
				channel.closeFuture().sync();

			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			} finally {
				logger.warn("channel shutdown gracefully");
				group.shutdownGracefully();
			}
		}).start();

		if (f == null) {
			return false;
		}
		
		return f.isSuccess();
	}

	public void close() {
		channel.close();
	}

	public void addListener(RobotEventListener listener) {
		this.listener = listener;
	}

	private <T> BaseResponse<T> send(BaseRequest<?> request) {
		BaseResponse<T> response = null;
		try {
			ChannelFuture channelFuture = channel.writeAndFlush(JSON.toJSONString(request) + "\r\n");
			channelFuture.addListener((future) -> {
				if (!future.isSuccess()) {
					logger.error("send message error.");
				}
			});

			SyncContext<T> context = new SyncContext<>();
			ShareData.syncContextMap.put(request.getId(), context);

			synchronized (context) {
				if (context.getResponse() == null) {
					try {
						context.wait(context.getTimeout());
					} catch (InterruptedException e) {
						logger.error("waiting response interrupted, request.id=" + request.getId(), e);
					}
				}
			}

			response = context.getResponse();

			if (response == null) {
				logger.error("service time out, response is null. request.id={}", request.getId());
			}

		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		} finally {
			ShareData.syncContextMap.remove(request.getId());
		}

		return response;
	}
	
	public BaseResponse<Void> authorization(String sceretKey) {
		Authentication body = new Authentication();
		body.setSceretKey(sceretKey);
		return send(new BaseRequest<>(RequestName.authentication, body));
	}

	public BaseResponse<Void> startup(long robotId, Long scenarioId) {
		Startup body = new Startup();
		body.setRobotId(robotId);
		body.setScenarioId(scenarioId);
		return send(new BaseRequest<>(RequestName.startup, body));
	}

	public BaseResponse<Void> startupAll(long scenarioId) {
		StartupAll body = new StartupAll(scenarioId);
		return send(new BaseRequest<>(RequestName.startupAll, body));
	}

	public BaseResponse<Void> shutdown(long robotId) {
		Shutdown body = new Shutdown();
		body.setRobotId(robotId);
		return send(new BaseRequest<>(RequestName.shutdown, body));
	}

	public BaseResponse<Void> shutdownAll() {
		return send(new BaseRequest<>(RequestName.shutdownAll, null));
	}

	public BaseResponse<Void> register(long robotId) {
		Register body = new Register();
		body.setRobotId(robotId);
		return send(new BaseRequest<>(RequestName.register, body));
	}

	public BaseResponse<Void> unregister(long robotId) {
		Unregister body = new Unregister();
		body.setRobotId(robotId);
		return send(new BaseRequest<>(RequestName.unregister, body));
	}

	public BaseResponse<String> call(long robotId, String phoneNumber) {
		Call body = new Call();
		body.setRobotId(robotId);
		body.setPhoneNumber(phoneNumber);
		return send(new BaseRequest<>(RequestName.call, body));
	}

	public BaseResponse<Void> dtmf(long robotId, char digit) {
		Dtmf body = new Dtmf();
		body.setRobotId(robotId);
		body.setDigit(digit);
		return send(new BaseRequest<>(RequestName.dtmf, body));
	}

	public BaseResponse<Void> hangup(long robotId) {
		Hangup body = new Hangup();
		body.setRobotId(robotId);
		return send(new BaseRequest<>(RequestName.hangup, body));
	}

	public BaseResponse<Void> stopTalking(long robotId) {
		StopTalking body = new StopTalking();
		body.setRobotId(robotId);
		return send(new BaseRequest<>(RequestName.stopTalking, body));
	}

	public BaseResponse<Void> sayFile(long robotId, String filePath, boolean canBeInterrupt) {
		SayFile body = new SayFile();
		body.setRobotId(robotId);
		body.setFilePath(filePath);
		body.setCanBeInterrupt(canBeInterrupt);
		return send(new BaseRequest<>(RequestName.sayFile, body));
	}

	public BaseResponse<Void> sayUrl(long robotId, String url, boolean canBeInterrupt) {
		SayUrl body = new SayUrl();
		body.setRobotId(robotId);
		body.setUrl(url);
		body.setCanBeInterrupt(canBeInterrupt);
		return send(new BaseRequest<>(RequestName.sayUrl, body));
	}

	public BaseResponse<Void> sayText(long robotId, String text, boolean canBeInterrupt) {
		SayText body = new SayText();
		body.setRobotId(robotId);
		body.setText(text);
		body.setCanBeInterrupt(canBeInterrupt);
		return send(new BaseRequest<>(RequestName.sayText, body));
	}

}